package com.zhanglu.fun.services.authentication.token;

import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.zhanglu.fun.services.authentication.bean.ApplicationRole;
import com.zhanglu.fun.services.authentication.bean.AuthToken;
import com.zhanglu.fun.services.authentication.bean.User;
import com.zhanglu.fun.services.authentication.client.DataCenterClient;
import com.zhanglu.fun.tookit.jwt.AuthenticationUser;
import com.zhanglu.fun.tookit.jwt.JwtUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * @author zhanglu
 */
@Service
@Slf4j
public class TokenSecureService {

    @Autowired
    private DataCenterClient dataCenterClient;

    private final int timeOutType = Calendar.MINUTE;//过期时间类型
    private final int minuteTimeOut = 60;//数据库中华令牌过期
    private final int tokenTimeOut = -10;//token里面令牌和数据库中令牌时间差,(minuteTimeOut + tokenTimeOut)是token中令牌过期时间

    public String getToken(String username, String password) {
        User user = dataCenterClient.getUserByAccount(username, password);
        if (user == null) {
            return null;
        }
        AuthToken authToken = dataCenterClient.getAuthTokenByUserId(user.getId());
        return Objects.isNull(authToken) ? null : authToken.getToken();
    }

    public String registerToken(String username, String password) {
        User user = dataCenterClient.getUserByAccount(username, password);
        if (user == null) {
            log.debug("can not find user by-->username is {},password is {}", username, password);
            return null;
        }
        AuthToken authToken = dataCenterClient.getAuthTokenByUserId(user.getId());
        if (null != authToken) {
            log.debug("token has already existing! delete the old token!");
            boolean result = deleteToken(authToken.getToken());
            log.debug("delete token result:{}", result);
        }

        Date expireDate = getExpiresDate().getTime();

        AuthenticationUser authenticationUser = new AuthenticationUser();
        BeanUtils.copyProperties(user, authenticationUser);
        authenticationUser.setUsername(user.getNickName()).
                setExpireDate(getTokenExpireDate(expireDate).getTime());

        String token = JwtUtil.createJWT(authenticationUser, getRoles(user.getId()));

        boolean result = dataCenterClient.addAuthTokenByToken(new AuthToken()
                .setToken(token)
                .setExpiresDate(expireDate)
                .setUserId(user.getId()));
        log.debug("{} user create token {},and token is {}", user.getNickName(), result, token);

        return result ? token : null;
    }

    public String refreshToken(String token){
        AuthToken authToken = dataCenterClient.getAuthTokenByToken(token);
        if (authToken != null) {
            long tokenDbTime = authToken.getExpiresDate().getTime();
            long now = System.currentTimeMillis();
            if (now > tokenDbTime) {
                log.debug("DB token time out!");
                return null;
            }
            
            Date expireDate = getExpiresDate().getTime();
            AuthenticationUser oldUser = JwtUtil.parserToken(token);
            oldUser.setExpireDate(getTokenExpireDate(expireDate).getTime());

            String newToken = JwtUtil.createJWT(oldUser, getRoles(authToken.getUserId()));
            authToken.setExpiresDate(expireDate);
            authToken.setToken(newToken);
            boolean result = dataCenterClient.updateAuthTokenById(authToken);
            log.debug("refresh token result is {}", result);
            return result ? newToken : null;
        }
        return null;
    }

    public boolean deleteToken(String token){
        return dataCenterClient.deleteAuthTokenByToken(token);
    }

    private List<String> getRoles(Long userId) {
        return dataCenterClient.getApplicationRolesByUserId(userId).
                stream().map(ApplicationRole::getRoleName).collect(Collectors.toList());
    }

    private Calendar getExpiresDate() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(timeOutType, minuteTimeOut);
        return calendar;
    }

    private Calendar getTokenExpireDate(Date expireDate) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(expireDate);
        calendar.add(timeOutType, tokenTimeOut); // 时间差10分钟赋给token。
        return calendar;
    }
}
